use std::fs;

/// Types of token
/// Meant to represent the various commands available in apollon.
#[derive(PartialEq)]
pub enum DocToken {
    // preface section
    /// Title of a document
    Title(String),
    /// Author of a document
    Author(String),
    /// Date of a document
    Date(String),

    // non-text commands
    /// Comment (undisplayed)
    Comment,
    /// Represents a newline in the compiled document
    Newline,

    // headers
    /// Numbered Header
    NHeader(String),
    /// Un-numbered header
    Header(String),

    // text formatting
    /// **Bold** face
    Bold(String),
    /// **Italics**
    Italic(String),
    /// _**Bold, and italic**_
    BoldItalic(String),
    /// Plain Text
    Text(String),
}

impl DocToken {
    // Processes a non command, ie a line without a command
    fn process_non_command(content: &str) -> DocToken {
        if content.trim().is_empty() {
            DocToken::Newline
        } else {
            DocToken::Text(String::from(content))
        }
    }

    /// Generates a token from command and content supplied,
    /// if command is unrecognised or empty, plain text is returned.
    pub fn from_command(command: &str, content: &str) -> DocToken {
        let content = String::from(content.trim()) + " ";
        //TODO: make writing switches more efficient

        match command {
            "title" => DocToken::Title(content),
            "author" => DocToken::Author(content),
            "date" => DocToken::Date(content),

            "#" => DocToken::Comment,
            "newline" | "nl" => DocToken::Newline,

            "nheader" | "nhead" => DocToken::NHeader(content),
            "header" | "head" => DocToken::Header(content),

            "bold" | "bl" => DocToken::Bold(content),
            "italic" | "il" => DocToken::Italic(content),
            "bold-italic" | "bi" => DocToken::BoldItalic(content),
            _ => DocToken::process_non_command(command),
        }
    }

    /// Parses a file given by `path`
    /// Returns `Vec<DocToken>`
    pub fn from_file(path: &str) -> Vec<DocToken> {
        // file contents
        let contents =
            fs::read_to_string(path).expect(format!("Could not open file {}!", path).as_str());
        let lines: Vec<&str> = contents.lines().collect();

        DocToken::from_lines(lines)
    }

    /// Parses lines given to the function into a Vec of doc tokens
    pub fn from_lines(lines: Vec<&str>) -> Vec<DocToken> {
        lines
            .into_iter()
            .map(|line| {
                let line: Vec<&str> = line.split(":").collect();
                let command = line[0];
                let content = &line[1..].join(":");

                DocToken::from_command(command, content)
            })
            .collect()
    }
}
